﻿namespace TomatoBreak.Infrastructure
{
    using System;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.Routing;

    using Castle.Facilities.FactorySupport;
    using Castle.Facilities.TypedFactory;
    using Castle.MicroKernel.Registration;
    using Castle.MicroKernel.Resolvers.SpecializedResolvers;
    using Castle.Windsor;
    using Castle.Windsor.Installer;

    public class Bootstrapper : Disposable
    {
        private readonly IWindsorContainer container;

        private static readonly object syncLock = new object();
        private static Bootstrapper instance;

        public Bootstrapper(IWindsorContainer container, IBuildManager buildManager)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }

            if (buildManager == null)
            {
                throw new ArgumentNullException("buildManager");
            }

            var resolver = new WindsorDependencyResolver(container);

            AddResolvers(container);
            AddFacilities(container);

            container.Register(Component.For<IBuildManager>().Instance(buildManager).LifeStyle.Singleton)
                     .Register(Component.For<IDependencyResolver>().Instance(resolver).LifeStyle.Singleton)
                     .Register(Component.For<IWindsorContainer>().Instance(container).LifeStyle.Singleton);

            RegisterTypeMappers(container);
            RegisterInfrastructuralServices(container);
            InstallInstallers(container, buildManager);
            RegistrarRegistrars(container);

            DependencyResolver.SetResolver(resolver);
            this.container = container;
        }

        public static void Initialize()
        {
            if (instance != null)
            {
                return;
            }

            lock (syncLock)
            {
                if (instance == null)
                {
                    instance = new Bootstrapper(new WindsorContainer(), new BuildManager());
                }
            }
        }

        public static void Cleanup()
        {
            if (instance == null)
            {
                return;
            }

            lock (syncLock)
            {
                if (instance != null)
                {
                    instance.Dispose();
                }
            }
        }

        protected override void DisposeCore()
        {
            container.Dispose();
        }

        private static void AddResolvers(IWindsorContainer container)
        {
            var resolver = container.Kernel.Resolver;

            resolver.AddSubResolver(new ArrayResolver(container.Kernel));
            resolver.AddSubResolver(new CollectionResolver(container.Kernel));
        }

        private static void AddFacilities(IWindsorContainer container)
        {
            container.AddFacility<FactorySupportFacility>()
                     .AddFacility<TypedFactoryFacility>();
        }

        private static void RegisterTypeMappers(IWindsorContainer container)
        {
            container.Register(Component.For<TypeMappingRegistry<object, IModelBinder>>().Instance(new TypeMappingRegistry<object, IModelBinder>()).LifeStyle.Singleton)
                     .Register(Component.For<FilterRegistry>().UsingFactoryMethod(k => new FilterRegistry(k.Resolve<IDependencyResolver>())).LifeStyle.Singleton);
        }

        private static void RegisterInfrastructuralServices(IWindsorContainer container)
        {
            container.Register(Component.For<HttpContextBase>().UsingFactoryMethod(k => new HttpContextWrapper(HttpContext.Current)).LifeStyle.Transient)
                     .Register(Component.For<RouteCollection>().Instance(RouteTable.Routes).LifeStyle.Singleton)
                     .Register(Component.For<GlobalFilterCollection>().Instance(GlobalFilters.Filters).LifeStyle.Singleton)
                     .Register(Component.For<IFormsAuthentication>().ImplementedBy<FormsAuthentication>().LifeStyle.Singleton)
                     .Register(Component.For<ICurrentUserProvider>().ImplementedBy<CurrentUserProvider>().LifeStyle.PerWebRequest);
        }

        private static void InstallInstallers(IWindsorContainer container, IBuildManager buildManager)
        {
            foreach (var assembly in buildManager.ApplicationAssemblies)
            {
                container.Install(FromAssembly.Instance(assembly));
            }
        }

        private static void RegistrarRegistrars(IWindsorContainer container)
        {
            foreach (var registrar in container.ResolveAll<IRegistrar>())
            {
                registrar.Register();
            }
        }
    }
}